/**
 * 1.输入
 * 得到用户输入的字符, 从键盘输入
 * 1. 将 "回车" & "空格" 都认为是字符串输入结束标志
 * 2. 如果用户输入的字符串长度 > 参数中数组长度, scanf 就会缓冲区溢出
 * 3. VS运行时报错: error C4996: 'scanf': This function or variable may be unsafe.
 *                  Consider using scanf_s instead.
 *                  To disable deprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details.
 *    解决方法:
 *      报错原因: 输入的可能不是汉字, 不安全!
 *      1.在.c文件第一行添加一个宏忽略错误(VS特有解决方法): #define _CRT_SECURE_NO_WARNINGS
 *      2.main()方法上方 or #include下方, 屏蔽错误(VS特有解决方法): #pragma warning(disable:4996)
 *      3.scanf_s
 */
#include <stdio.h>

//输入
void scanf_test();
void getchar_test();
void sscanf_test();
void gets_test();
void fgets_test();
//输出
void printf_test();
void putchar_test();
void puts_test();
void fputs_test();
void sprintf_test();

int main(void) {
    /**
     * 1.输入     是否安全        说明
     */
    //scanf       不安全         格式化输入
//    scanf_test();
//    //getchar                   获取一个字符
//    getchar_test();
//    //sscanf:                   从指定格式化字符串读取输入
//    sscanf_test();
//    //gets         不安全          get s, 可接收空格
//    gets_test();
//    //fgets        安全           file get String, 可接收空格, \n
//    fgets_test();


    /**
     * 2.输出
     */
     //printf                   格式化输出
    printf_test();
    //putchar
//    putchar_test();
//    //puts                      put String,  遇到'\0'停止,  自动加'\n'
//    puts_test();
//    //fputs                     file put String
//    fputs_test();
//    //sprintf                   将格式化结果输出到字符串, 可用于 int-->String
//    sprintf_test();

    return 0;
}

//scanf: 格式化输入
void scanf_test() {
    //                              输入              输出      说明
    int len, a, b;
    char str[100] = { 0 };
    scanf("%d",&len);       //  22                22        &: 取地址符
    scanf("%d,%d",&a, &b);  //12,34              12 34
    scanf("%d%d",&a, &b);   //12 34 or 12回车34   12 34      默认以 空格 or '\n' 分割开
    scanf("%d\n%d",&a, &b); //12 34 or 12回车34   12 34
    scanf("%d\n", &a);      //12\n                          会一直输入回车, (会出问题!!!)
    scanf("%3d,%d",&a, &b); //123456             123 456
    scanf("%s", str);       //输入字符串           字符串      因为str就是地址, 所以不用写&str
    scanf("%9s", str);      //12345678901        123456789  限定最多取9位
    scanf("%9c", str);      //12345678901        123456789  限定最多取9位(可读取' ')

    scanf("%[^\n]", str);   //正则表达式, 能接受空格, 遇到回车结束.
}

//getchar()也有等待的效果!
void getchar_test() {
    char charA = getchar();
    printf("charA = %c\n", charA);
}

/**
* 3.sscanf: 从指定格式化字符串读取输入
*/
void sscanf_test() {
    int len, a, b;
    char array1[100] = "abc=500";
    char array2[100] = "5+6=";
    sscanf(array1, "abc=%d", &len); //len = 500
    sscanf(array2, "%d+%d", &a, &b);//a=5, b=6
    printf("sscanf 测试: len = %d, a = %d, b = %d\n", len, a, b);

    char array3[] = "Hello World!";
    char dest1[100];
    sscanf(array3, "%s", dest1);
    printf("dest1 = %s\n", dest1);    //Hello,  默认以 空格 or '\n' 分割开
    sscanf(array3, "%[^\n]", dest1);
    printf("dest1 = %s\n", dest1);    //Hello World!

    char dest2[100];
    char array4[] = "HelloWorld!";
    scanf(array4, "%5s%s", dest1, dest2);
    printf("dest1 = %s, dest2 = %s\n\n", dest1, dest2); //Hello, World!
}

/**
 * 4.gets:  get s, https://www.bilibili.com/video/BV1vs411n7TH?p=50
 * 从键盘获取一个 字符串
 * 1. 只将 "回车"作为字符串输入结束标志, 可获得"空格"
 * 2. 和scanf一样存在缓冲区溢出问题
 * 3. 参数只能是数组, 不能用"%s" 或 "%d" 转义...???
 * @return 字符指针?
 */
void gets_test() {
    char str[100] = { 0 };
    char *result = gets(str);
    printf("str = %s\n", str);
}

/**
 * fgets: file get String
 *      可以接收 空格,
 *      以'\n'结尾(将用户输入的 回车 也一起算进去),
 *      安全, 不存在缓冲区溢出问题.
 */
void fgets_test() {
    char str[10] = { 0 };
    /**
     * 参1: 字符串.
     * 参2: 指定最大读取字符串长度.
     * 参3: strean 文件指针, 如果读键盘输入的字符串, 固定写为 stdin
     * 示例:
     *      输入: hello回车        输出: hello\n  (如果没输满, 最后的 \n 得以保留)
     *      输入: helloworld,     输出: helloword(最后一位留给 \0, 输入再多也不会崩溃)
     */
    fgets(str, 10, stdin);
}

/**
 * 2.printf: 格式化输出
 */
void printf_test() {
    char charA = 'A';
    short shortI = 2;
    int len = 5;
    int hex = 27;
    long long1 = 123456L;
    long long longlong = 123456789LL;
    float floatF = 5.1215926F;
    float floatF2 = -5.23456789F;
    float floatF3 = 0.23456789F;
    float floatF4 = 5.23456789F;
    float floatF5 = 5.2340000F;
    double lf = 3.2435718765676567F;
    char array[100] = {'a', 'b', 'c', 'd'};
    char array2[12] = "Hello World!";     //因为"Hello World!"的长度=12, 所以装不下'\0', 会出问题
    char * str   = "Hello\0 World!\n"; //'\0' 是结束标志, 一般字符串末尾都有一个'\0'
    unsigned int ui = 10U;
    unsigned short us = 10U;
    unsigned long ul = 10UL;
    unsigned long long ull = 10ULL;

    printf("1. 1个参数:\n");
    printf("Hello World!\n");   //字符串: format print, 格式化输出字符串
    printf("\?\n");             // \? : 问号.  ( 直接打印也可以: printf("?");  )
    printf("\'\n");             // \' : 单引号.( 直接打印也可以: printf("'");  )
    printf("\\\n");             // \\ : 斜杠
    printf("\"\n");             // \" : 双引号
    printf("\a\n");             // \a : 报警, 电脑响一声, \a=7
    printf("\b");               // \b : 退格
    printf("\n");               // \n : 换行
    printf("\r");               // \r : 回车 回到这一行最开始的位置并从第一个字符再次开始替换掉接下来输入的内容, 不换行
    printf("\t");               // \t : 制表符
    printf("%%\n");             // %% : 输出一个百分比(%)
    printf("\167\n");           //\ddd: (w) 3位8进制转义符, d(0~7)。将ddd三个8进制转换成十进制(119)，十进制数对应的ASCAII就是结果(w)。
    printf("\x67\n");           //\xhh: (g) 3位16进制转义符, h(0~F)。将hh转换成十进制(103)，十进制数对应的ASCAII就是结果(g)。

    printf("\n2. 2个参数:\n");
    printf("%c\n", charA);      //%c  : (A) 输出 char 类型
    printf("%hd\n", shortI);    //%hd : (2) 输出一个 短整型(half int) 类型
    printf("%d\n", len);        //%d  : (5) 输出一个"有符号"的10进制int类型
    printf("%ld\n", long1);     //%ld : (123456)
    printf("%lld\n", longlong); //%lld: (123456789), long long
    printf("%f\n", floatF);     //%f  : (5.121592) float, (默认小数点后6位)
    printf("%lf\n", lf);        //%lf : (3.243572) double, 输出一个 double(long float) 类型 (默认小数点后6位)
    printf("%o\n", len);        //%o  : (5) 输出一个 8进制的整数
    printf("%s\n", array);      //%s  : (abcd) 打印数组/字符串(char * / wchar_t *), 输出字符串中的字符直至字符串中的结尾空字符('\0')
    printf("%s\n", array2);     //%s  : Hello World!烫烫烫烫烫?烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫橅刏喧? (因为结尾装不下'\0')
    printf("%s\n", str);        //%s  : (Hello) 打印 字符串 的时候, 遇到结束标志'\0'就结束打印!!!
    printf("%x\n", hex);        //%x  : (1b) 输出16进制整数(int, long int, short int?), 字母以小写输出
    printf("%X\n", hex);        //%X  : (1B) 输出16进制整数(int, long int, short int?), 字母以大写输出

    printf("\n3. unsigned(无符号):\n");//l: 附加在"d'u'x'o"前, 表示长整型
    printf("%u\n", ui);         //%u  : (10) unsigned int, 输出10进制无符号数"十进制"整型数据 (最高位不当作符号位, 一个小负数会输出一个大整数)
    printf("%hu\n", us);        //%hu : (10) unsigned short, 无符号短整数
    printf("%lu\n", ul);        //%lu : (10) unsigned long
    printf("%llu\n", ull);      //%llu: (10) unsigned long long

    printf("\n4. 扩展:\n");
    printf("%d\n", !len);       //    : (0)    shortI 是真值, !shortI 是假
    printf("%+d\n", len);       //%+d : (+5)   输出带符号, 正号&负号
    printf("%03d\n", len);      //%03d: (005)   前面补0, 直到占满指定列宽为止(不可搭配使用"-")
    printf("%2d\n", len);       //%2d : ( 5)    右对齐, m(整数), 数据最小宽度, 前面空格
    printf("(%-2d)\n", len);    //%-2d: (5 )    左对齐输出, 后面空格
    printf("%.3f\n", floatF);   //%.3f: (5.122) 输出的小数点后面是3位, 会四舍五入
    printf("%8.3f\n", floatF);  //%m.nf: (   5.122) m:域宽,输出总长度(包括小数点),位数不够前方补空格.  n:精度,输出的小数位数,默认=6

    printf("\n5. 其它:\n");
    printf("%e\n", lf);         //%e  : (3.243572e+000) float/double 科学计数法表示的数, 此处"e"的大小写代表在输出时e的大小写
    printf("%E\n", lf);         //%E  : (3.243572E+000)
    /**
     * %g: f和e类型中较短的, 后面"连续0"会被舍弃
     * if(整数部分=0) 小数点后默认6位 (第7位会被 四舍五入)
     * if(整数部分≠0) 小数点后默认5位 (第6位会被 四舍五入)
     */
    printf("%g\n", floatF2);    //%g  : (-5.23457)
    printf("%g\n", floatF3);    //%g  : (0.234568)
    printf("%g\n", floatF4);    //%g  : (5.23457)
    printf("%g\n", floatF5);    //%g  : (5.234) 后面"连续0"会被舍弃
    printf("%.17g\n", lf);      //%.7g: (3.2435717582702637) 过长会打印出错!
    printf("%p\n", &lf);        //%p  : (0061FE80) 以16进制形式输出"内存地址编号"
    printf("%#x\n", hex);       //%#x : (0x1b) 打印int的16进制值
}

//putchar
void putchar_test() {
    putchar(67);    //C     打印ASC2码
    putchar('A');   //A
    putchar('\n');  //换行
}

/**
 * puts: put String
 * 1. 只能输出字符串, 不能输出整数
 * 2. 遇到'\0'停止
 * 3. 会在最后自动添加回车 '\n'
 * @return 成功: 非负数.  失败: -1
 */
void puts_test() {
    char * array   = "Hello World!";
    puts(array);
    puts("");   //换行
}

/**
 * https://www.bilibili.com/video/BV1vs411n7TH?p=51
 * fputs: file put String, 将str所指定的字符串写入到stream指定的文件中
 * 1.字符串结束符'\0'不写入文件.
 * 参1: 字符串
 * 参2: 文件指针, 如果把字符串输出到屏幕, 固定写为 stdout
 */
void fputs_test() {
    char * array   = "Hello World!";
    fputs(array, stdout);
}

/**
 * sprintf 字符串格式化 https://www.bilibili.com/video/BV1vs411n7TH/?p=85 <br />
 * 将格式化结果输出到字符串, 可用于int --> String <br />
 * <br />
 * int __cdecl sprintf(char * __restrict__ _Dest,const char * __restrict__ _Format,...) __MINGW_ATTRIB_DEPRECATED_SEC_WARN;
 * @param _Dest  : 将结果输出到Dest
 * @param _Format: 格式化字符串, 例: %d%c%d=\n
 * @param ...    : 格式化可变参数, 例: 2, '+', 5
 * @return 成功: 返回写入的字符总数，不包括字符串追加在字符串末尾的空字符。<br />
 *         失败: 返回一个负数
 */
void sprintf_test() {
    char arr[100];
    int len0 = sprintf(arr, "hello world");
    printf("arr = %s\n", arr);

    //将"1+2=3"拷贝到 字符数组.
    int len1 = sprintf(arr, "%d+%d=%d", 1, 2, 3);
    printf("arr = %s\n", arr);

    int i = 0;
    /**
     * 3.格式化字符串, 并输出到 arr
     */
    int len2 = sprintf(arr, "Y:\\Users\\actor\\Desktop\\temp\\%d.jpg", i % 2 + 1);
    printf("arr = %s\n", arr);
}


//还未讲过
void unTeach() {
    /**
     * c11 标准删除了 gets() 函数，更换成了gets_s()
     * 但使用会出现错误: undefined reference to `_imp__gets_s'，用不了，反而是gets() 还能用。
     * 因为目前GCC中还没有完全实现此标准， 因此 gets_s() 函数尚未包含在目前的GNU 工具链中。Clang里也暂时没有增加对 gets_s 的支持
     * 可以自己实现一个:
     * char *gets_s(char * str, int num) {
     *     if (fgets(str, int, stdin) != 0) {
     *         size_t len = strlen(str);
     *         if (len > 0 && buffer[len-1] == 'n')
     *             buffer[len-1] = '';
     *         return buffer;
     *     }
     *     return 0;
     * }
     */
//    char * str = "Hello";
    char str[11] = { 0 };
    /**
     * 只从标准输入中读取数据, 读到换行符，会丢弃它. _s: Standard
     * 参1: 读取到哪个数组
     * 参2: 最多读取的数量，一般为数组大小(unsigned int)
     */
//    char* result = gets_s(str, 10);
//    printf(result);
}

